package com.knife.oauth.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.knife.cache.redis.constants.RedisConstant;
import com.knife.common.exception.CommonException;
import com.knife.message.domain.service.KnifeMessageSendHelper;
import com.knife.oauth.api.controller.IamUserRegisterDto;
import com.knife.oauth.api.dto.ForgetPasswordDto;
import com.knife.oauth.app.service.AfterUserRegisterService;
import com.knife.oauth.app.service.IamUserService;
import com.knife.oauth.domain.entity.IamMemberRole;
import com.knife.oauth.domain.entity.IamRole;
import com.knife.oauth.domain.entity.IamUser;
import com.knife.oauth.domain.entity.KnifeUserDetails;
import com.knife.oauth.infra.cache.v2.KnifeIamUserRedisCache;
import com.knife.oauth.infra.mapper.IamMemberRoleMapper;
import com.knife.oauth.infra.mapper.IamRoleMapper;
import com.knife.oauth.infra.mapper.IamUserMapper;
import com.knife.oauth.util.DetailHelper;
import com.knife.redis.KnifeRedisHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.util.ProxyUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用户service
 *
 * @author： 76875
 * @date： 2022/5/26 星期四 21:02
 * @description：
 * @modifiedBy：
 * @version: 1.0
 */
@Service
@Slf4j
@SuppressWarnings("ALL")
public class IamUserServiceImpl implements IamUserService, ApplicationContextAware {

    public static final int REDIS_DB = 1;
    @Autowired
    private IamUserMapper iamUserMapper;

    @Autowired
    private KnifeIamUserRedisCache knifeIamUserRedisCache;

    @Autowired
    private IamMemberRoleMapper iamMemberRoleMapper;

    @Autowired
    private IamRoleMapper iamRoleMapper;

    @Autowired
    private KnifeMessageSendHelper knifeMessageSendHelper;

    @Autowired
    private KnifeRedisHelper knifeRedisHelper;

    private ApplicationContext applicationContext;

    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public void changePassword(String password) {
        KnifeUserDetails userDetails = DetailHelper.getUserDetails();
        // 获取数据用户信息
        IamUser currUser = iamUserMapper.selectById(userDetails.getUserId());
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode(password);
        currUser.setHashPassword(encode);
        iamUserMapper.updateById(currUser);
        // 刷新用户缓存
        knifeIamUserRedisCache.setCache(currUser);
    }

    @Override
    public Page<IamUser> selectIamUserPage(Page<IamUser> iamUserPage, IamUser iamUser) {
        LambdaQueryWrapper<IamUser> iamUserLambdaQueryWrapper = new LambdaQueryWrapper<>();
        iamUserLambdaQueryWrapper
                .like(StringUtils.isNotEmpty(iamUser.getRealName()),IamUser::getRealName,iamUser.getRealName())
                .like(StringUtils.isNotEmpty(iamUser.getEmail()),IamUser::getEmail,iamUser.getEmail())
                .like(StringUtils.isNotEmpty(iamUser.getLoginName()),IamUser::getLoginName,iamUser.getLoginName())
                .eq(IamUser::getTenantId,iamUser.getTenantId())
                .orderByDesc(IamUser::getCreationDate);
        return iamUserMapper.selectPage(iamUserPage,iamUserLambdaQueryWrapper);
    }

    @Override
    public void changePassword(ForgetPasswordDto forgetPasswordDto) {
        // 注册之前需要校验验证码是否存在，不存在则提示验证码无效
        if (validateForgetVerifyCode(forgetPasswordDto.getEmail(),forgetPasswordDto.getVerifyCode())) {
            LambdaQueryWrapper<IamUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(IamUser::getEmail,forgetPasswordDto.getEmail());
            // 获取数据用户信息
            IamUser currUser = iamUserMapper.selectOne(lambdaQueryWrapper);
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            String encode = passwordEncoder.encode(forgetPasswordDto.getPassword());
            currUser.setHashPassword(encode);
            iamUserMapper.updateById(currUser);
            // 刷新用户缓存
            knifeIamUserRedisCache.setCache(currUser);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public void registerUser(IamUserRegisterDto iamUser) {
        // 注册之前需要校验验证码是否存在，不存在则提示验证码无效
        if (validateRegisterVerifyCode(iamUser.getEmail(),iamUser.getVerifyCode())) {
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            String encode = passwordEncoder.encode(iamUser.getPassword());
            iamUser.setHashPassword(encode);
            // 注册用户
            iamUserMapper.insert(iamUser);
            // 获取用户角色
            QueryWrapper<IamRole> iamRoleQueryWrapper = new QueryWrapper<>();
            iamRoleQueryWrapper.eq("code","user");
            IamRole iamRole = iamRoleMapper.selectOne(iamRoleQueryWrapper);
            // 用户角色信息
            IamMemberRole iamMemberRole = new IamMemberRole();
            iamMemberRole.setRoleId(iamRole.getId());
            iamMemberRole.setUserId(iamUser.getId());
            iamMemberRole.setTenantId(iamUser.getTenantId());
            iamMemberRoleMapper.insert(iamMemberRole);
            // 刷新用户缓存
            // 获取数据用户信息
            IamUser currUser = iamUserMapper.selectById(iamUser.getId());
            knifeIamUserRedisCache.setCache(currUser);
            // 用户注册后,执行扩展事件
            Map<String, AfterUserRegisterService> afterUserRegisterServiceList = applicationContext.getBeansOfType(AfterUserRegisterService.class);
            Collection<AfterUserRegisterService> userRegisterServices = afterUserRegisterServiceList.values();
            if (CollectionUtils.isNotEmpty(userRegisterServices)) {
                List<AfterUserRegisterService> afterUserRegisterServices = userRegisterServices
                        .stream()
                        .sorted(Comparator.comparing(AfterUserRegisterService::order))
                        .collect(Collectors.toList());
                for (AfterUserRegisterService afterUserRegisterService : afterUserRegisterServices) {
                    log.debug("--------------执行注册后方法的类为:{}----------------", ProxyUtils.getUserClass(afterUserRegisterService).getName());
                    afterUserRegisterService.doAfterUserRegister(currUser);
                }
            }
        }else {
            throw new CommonException("验证码错误或失效!");
        }
    }

    @Override
    public void sendRegisterVerifyCode(String email) {
        knifeRedisHelper.setCurrentDatabase(REDIS_DB);
        try {
            Map<String, String> map = new HashMap<>(2);
            map.put("title","登录验证码");
            // 随机生成验证码
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 6; i++) {
                int nextInt = RandomUtils.nextInt(0, 9);
                sb.append(nextInt);
            }
            String verifyCode = sb.toString();
            map.put("code", verifyCode);
            // 将验证码和redis绑定
            knifeRedisHelper.strSet(buildRegisterVerifyCodeKey(email),verifyCode, 30, TimeUnit.MINUTES);
            knifeMessageSendHelper.sendEmail("REGISTER_CAPTCHA",null,map,email);
        } finally {
            knifeRedisHelper.clearCurrentDatabase();
        }
    }
    
    private String buildRegisterVerifyCodeKey(String email){
        return RedisConstant.OAUTH+":register:verifyCode:"+email;
    }

    private String buildForgetVerifyCodeKey(String email){
        return RedisConstant.OAUTH+":forget:verifyCode:"+email;
    }

    @Override
    public Boolean validateRegisterVerifyCode(String email, String verifyCode) {
        String verifyCodeKey = buildRegisterVerifyCodeKey(email);
        // 判断当前邮箱的验证码是否匹配
        knifeRedisHelper.setCurrentDatabase(REDIS_DB);
        try {
            // 将验证码和redis绑定
            String strGet = knifeRedisHelper.strGet(verifyCodeKey);
            if (verifyCode.equals(strGet)){
                // 验证码使用成功直接删除键
                knifeRedisHelper.delKey(verifyCodeKey);
                return true;
            }
            return false;
        } finally {
            knifeRedisHelper.clearCurrentDatabase();
        }
    }

    @Override
    public void sendForgetVerifyCode(String email) {
        knifeRedisHelper.setCurrentDatabase(REDIS_DB);
        try {
            Map<String, String> map = new HashMap<>(2);
            map.put("title","忘记密码验证码");
            // 随机生成验证码
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 6; i++) {
                int nextInt = RandomUtils.nextInt(0, 9);
                sb.append(nextInt);
            }
            String verifyCode = sb.toString();
            map.put("code", verifyCode);
            // 将验证码和redis绑定
            knifeRedisHelper.strSet(buildForgetVerifyCodeKey(email),verifyCode, 30, TimeUnit.MINUTES);
            knifeMessageSendHelper.sendEmail("FORGET_CAPTCHA",null,map,email);
        } finally {
            knifeRedisHelper.clearCurrentDatabase();
        }
    }

    @Override
    public Boolean validateForgetVerifyCode(String email, String verifyCode) {
        String verifyCodeKey = buildForgetVerifyCodeKey(email);
        // 判断当前邮箱的验证码是否匹配
        knifeRedisHelper.setCurrentDatabase(REDIS_DB);
        try {
            // 将验证码和redis绑定
            String strGet = knifeRedisHelper.strGet(verifyCodeKey);
            if (verifyCode.equals(strGet)){
                // 验证码使用成功直接删除键
                knifeRedisHelper.delKey(verifyCodeKey);
                return true;
            }
            return false;
        } finally {
            knifeRedisHelper.clearCurrentDatabase();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
